feat(generator): DiffTraverser/DiffVisitor for paired tree comparison (C36)#1139
Merged
Conversation
…36 Phase 1) Base classes for paired tree traversal: - CollectionDiff with Entry/MatchedPair for collection comparison results - PairingStrategy interface with KeyPairingStrategy (maps) and IndexPairingStrategy (lists) - DiffVisitor abstract base with field-level diff methods and strategy selection callback - AbstractDiffTraverser with shared diffMap/diffList/diffEntityField helpers and auto-recursion into matched pairs
New CreateDiffTraversersStage generates per-spec-version diff traversers that walk two trees in parallel, dispatching to DiffVisitor methods: - diffPrimitive for primitive fields - diffEntityField for entity fields (auto-recurses) - diffUnionField for union fields - diffMap for map fields (pairs by key, auto-recurses matches) - diffList for list fields (pairs by index, auto-recurses matches) Registered in pipeline after CreateTraversersStage. Base classes registered in LoadBaseClassesStage.
…ategies (C36)
Generated DiffVisitor per spec version with typed, field-specific methods:
- Entity-level: visitFullSchema(original, updated) → boolean
- Primitive: diffFullSchemaTitle(String, String), diffFullSchemaMinLength(Number, Number)
- Entity/Union: diffFullSchemaNot(JsonSchema, JsonSchema)
- Map: diffFullSchemaProperties(CollectionDiff<String, JsonSchema>) +
visitFullSchemaProperty(String key, JsonSchema, JsonSchema)
- List: diffFullSchemaAllOf(CollectionDiff<Integer, JsonSchema>) +
visitFullSchemaAllOfItem(Integer index, JsonSchema, JsonSchema)
Method names prefixed with entity name to avoid collisions across entity types.
DiffTraverser now generic (AbstractDiffTraverser<V extends DiffVisitor>),
calls typed visitor methods instead of generic diffPrimitive/diffMap/etc.
Extracted shared pairing logic into PairingStrategy.pairByKey() static method.
KeyPairingStrategy and IndexPairingStrategy both delegate to it.
…ngStrategy (C36) - MapPairingStrategy<V>.pair(Map, Map) — for map fields - ListPairingStrategy<V>.pair(List, List) — for list fields, handles index conversion internally - CollectionDiff.pairByKey() — shared static factory for the common key-based pairing logic - DiffVisitor: getMapPairingStrategy/getListPairingStrategy replace single getPairingStrategy
…terfaces (C36) - Delete PairingStrategy marker interface (unused) - MapPairingStrategy<P, V> — P is the pairing key type (String for default) - ListPairingStrategy<P, V> — P is the pairing key type (Integer for default) - Custom strategies can use any P that's meaningful to the visitor
Item visitor methods no longer receive the pairing key since it's strategy-dependent and not necessarily the map key or list index. Visitors that need the key context get it from the CollectionDiff in the collection-level diff method. Before: visitFullSchema$defs(String key, JsonSchema original, JsonSchema updated) After: visitFullSchema$defs(JsonSchema original, JsonSchema updated)
…sitor (C36) Strategy selection is now a separate concern from comparison logic: - PairingStrategyProvider interface with getMapStrategy/getListStrategy - DefaultPairingStrategyProvider: key-based for maps, index-based for lists - AbstractDiffTraverser takes provider at construction (defaults to DefaultProvider) - Generated traversers expose two constructors: (visitor) and (visitor, provider) - DiffVisitor is now a pure base for field-specific comparison methods
…gKey (C36) - PairingStrategyProvider<P> with P for the pairing key type - DefaultPairingKey with index (int) and key (String) variants - DefaultPairingStrategyProvider implements PairingStrategyProvider<DefaultPairingKey> - KeyPairingStrategy/IndexPairingStrategy produce CollectionDiff<DefaultPairingKey, V> - AbstractDiffTraverser<P, V> threads P through pairMap/pairList - Generated traversers use Object for P (erasure-safe, unchecked cast in default constructor) - Generated visitors use CollectionDiff<Object, V> for collection diff methods - DiffVisitor is now a pure marker base (strategy selection moved to PairingStrategyProvider) - Removed PairingStrategy marker interface
…P> (C36) Both generated classes are now parameterized by the pairing key type P: - JD6DiffTraverser<P> extends AbstractDiffTraverser<P, JD6DiffVisitor<P>> - JD6DiffVisitor<P> extends DiffVisitor CollectionDiff<P, V> flows through the entire stack — no Object erasure or unchecked casts needed. Usage: new JD6DiffTraverser<DefaultPairingKey>(visitor)
…ize item visitors (C36) - Collection diff methods now receive (original, updated, diff) instead of just (diff) - Map item visitor methods singularized: visitFullSchemaProperty (not Properties), visitFullSchemaDependency (not Dependencies) - CodeGenContext.singularize made static, reused from both stages
The diff infrastructure uses generics (P type parameter) that cause JSweet to hang during transpilation. The diff traverser/visitor are Java-only — not needed in TypeScript.
c41da7e to
c571a9d
Compare
…y) (C36) - Remove empty DiffVisitor base class — generated visitors are standalone - Rename IndexPairingStrategy → DefaultListPairingStrategy - Rename KeyPairingStrategy → DefaultMapPairingStrategy - Add traverse(Any, Any) public entry point on AbstractDiffTraverser (matches AbstractTraverser pattern, supports union roots like boolean schemas) - Remove V extends DiffVisitor bound — just V - Fix unchecked cast in default constructor
…elds (C36)
Generated DiffVisitor now has diff methods for each union type:
- diffJsonSchema(JsonSchema, JsonSchema)
- diffStringStringListUnion(StringStringListUnion, StringStringListUnion)
Generated DiffTraverser now has traverse methods for each union type:
- traverseJsonSchema calls visitor.diffJsonSchema, then auto-recurses
into traverseFullSchema if both sides are the same entity variant
- Union fields (not, if/then/else, additionalProperties, etc.) now
call traverseUnionType for auto-recursion instead of just notifying visitor
Handles true → {} (boolean schema to object schema): visitor.diffJsonSchema
gets called with mixed variants, no recursion occurs.
…rser (C36) Union list/map matched pairs now call traverseUnionType instead of being silently skipped. E.g., dependencies map pairs call traverseDependency, allOf list pairs call traverseJsonSchema. Entity collections still use traverseNode as before.
Generated traverse(Any, Any) now dispatches to both union and entity traverse methods. Union types are checked first (instanceof) since entity implementations also implement their union interfaces. Removed traverseNode from AbstractDiffTraverser — all dispatch is now in the generated traverse override. Entity field recursion and collection matched pair recursion call traverse() instead of traverseNode().
Entities that are variants of a union type are already matched by the union instanceof check. Don't emit redundant entity branches that would be dead code (e.g., JD7FullSchema is covered by JsonSchema).
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
New infrastructure for paired tree traversal — walks two trees of the same spec version in parallel, dispatching field-level diffs to a pluggable DiffVisitor.
Base classes (
visitors/diff/)DiffVisitor— abstract visitor with methods for each field type:diffPrimitive,diffEntity,diffUnion,diffList,diffMap. Strategy selection viagetPairingStrategy(propertyName).AbstractDiffTraverser— shareddiffMap/diffList/diffEntityFieldhelpers with auto-recursion into matched pairs.CollectionDiff<K,V>— diff result with added/removed/matched entries.PairingStrategy<K,V>— pluggable collection pairing. Built-in:KeyPairingStrategy(maps),IndexPairingStrategy(lists).Generated per spec version
CreateDiffTraversersStage— generatesXxxDiffTraverserper spec version (e.g.,JD7DiffTraverser), following the same entity-property iteration asCreateTraversersStage.traverseXxx(original, updated)method that dispatches to the visitor.traverseNode(original, updated)dispatches byinstanceofto the correct entity method.Example generated output
Context
C36 Phase 1-2 in #1042. Phase 3 (compat checker migration) will be a follow-up.
Test plan